package com.ecoolex.union.pay.retailer.ws;

import com.ecoolex.framework.common.util.Check;
import com.ecoolex.union.pay.retailer.PushOrderDTO;
import com.ecoolex.union.pay.retailer.core.manager.UnionPayCouponManager;
import com.ecoolex.union.pay.retailer.core.manager.UnionPayPopularCurrencyCodeManager;
import com.ecoolex.union.pay.retailer.core.properties.UnionPayUplanPropertis;
import com.ecoolex.union.pay.retailer.core.util.UnionPayUplanService;
import com.ecoolex.union.pay.retailer.core.util.unionpay.LogUtil;
import com.ecoolex.union.pay.retailer.entity.UnionPayCoupon;
import com.ecoolex.union.pay.retailer.enums.DirectMailGoodsOrderCouponType;
import com.ecoolex.union.pay.retailer.enums.UnionPayCouponStatus;
import com.ecoolex.union.pay.retailer.pojo.PushOrderChannelDTO;
import com.ecoolex.union.pay.retailer.pojo.PushOrderCouponDTO;
import com.ecoolex.union.pay.retailer.pojo.PushOrderDetailDTO;
import com.ecoolex.union.pay.retailer.pojo.UnionPayOrderPushGoodsData;
import com.ecoolex.union.pay.retailer.pojo.UnionPayOrderPushRequest;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.net.URI;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

@Service
@Slf4j
public class UnionPayOrderPushServiceImpl implements UnionPayOrderPushService {

    @Autowired
    UnionPayCouponManager unionPayCouponManager;

    @Autowired
    UnionPayUplanService unionPayUplanService;

    @Autowired
    UnionPayUplanPropertis unionPayUplanPropertis;

    @Autowired
    Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder;

    @Autowired
    RestTemplateBuilder restTemplateBuilder;

    @Override
    public void push(PushOrderDTO pushOrder,List<PushOrderCouponDTO> couponList,List<PushOrderDetailDTO> pushOrderDetailList,PushOrderChannelDTO channel) {
        UnionPayOrderPushRequest request = new UnionPayOrderPushRequest();
        request.setServiceId(pushOrder.getServiceId());
        request.setTimestamp(String.valueOf(System.currentTimeMillis()));
        if (Check.notEmpty(pushOrder.getExternalUserId())){
            request.setUserId(pushOrder.getExternalUserId());
        }else {
            if (Check.notNull(pushOrder.getUserId())){
                request.setUserId(String.valueOf(pushOrder.getUserId()));
            }else {
                request.setUserId("0");
            }
        }
        if (Check.notNull(channel)) {
            request.setChannelId(Optional.ofNullable(channel.getChannelId()).orElse("0"));
            request.setMerchantId(Optional.ofNullable(channel.getMerchantId()).orElse(""));
            request.setMerchantName(Optional.ofNullable(channel.getMerchantName()).orElse(""));
        } else {
            request.setChannelId("");
            request.setMerchantId("");
            request.setMerchantName("");
        }

        request.setOrderNo(pushOrder.getOrderNo());
        request.setStatus(pushOrder.getStatus());
        request.setCreateTime(pushOrder.getCreateTime());
        if(Check.notNull(pushOrder.getPaymentTime())){
            request.setPaymentTime(pushOrder.getPaymentTime());
        }
		request.setUpdateTime(pushOrder.getUpdateTime());

		String orderRootUrl = pushOrder.getOrderUrl();
		String orderNo = request.getOrderNo();
		Assert.hasText(orderRootUrl, "订单详情地址不能为空");
		Assert.hasText(orderNo, "订单号不能为空");
		orderRootUrl += String.format("?orderNo=%s", orderNo);
		String url = UriComponentsBuilder.fromUri(URI.create(orderRootUrl)).build(true).encode().toUriString();
        try {
            request.setOrderUrl(URLEncoder.encode(url,"UTF-8"));
        } catch (UnsupportedEncodingException e) {
            log.error("URLEncoder",e);
        }
        request.setTotalNumber(0);
        request.setOrderCurrency(UnionPayPopularCurrencyCodeManager.translateCurrencyCode(pushOrder.getCurrencyType()));
        request.setOrderAmount(pushOrder.getTotalPrice());

        if (Check.notNullOrEmpty(couponList)) {
        	for (PushOrderCouponDTO coupon : couponList) {
				if(Check.isEqual(coupon.getCouponType(), DirectMailGoodsOrderCouponType.UNION_PAY_COUPON.real())) {
					UnionPayCoupon unionPayCoupon = unionPayCouponManager.getCouponByUserId(pushOrder.getUserId(), coupon.getCouponId(), UnionPayCouponStatus.UNUSED, UnionPayCouponStatus.LOCKED,UnionPayCouponStatus.USED);
		            request.setCouponId(unionPayCoupon.getCouponCode());
		            request.setCouponName(unionPayCoupon.getCouponName());
		            request.setCouponAmount(unionPayCoupon.getReducePrice());
				}
			}
        }

        request.setPayAmount(pushOrder.getRealPrice());
        List<UnionPayOrderPushGoodsData> goodsDataList = new ArrayList<>();
        Integer totalBuyNumber = 0;
        if (Check.notNullOrEmpty(pushOrderDetailList)) {
            for (PushOrderDetailDTO detail : pushOrderDetailList) {
                UnionPayOrderPushGoodsData goodsData = new UnionPayOrderPushGoodsData();
                goodsData.setProductId(String.valueOf(detail.getDirectMailGoodsId()));
                goodsData.setProductName(detail.getName());
                goodsData.setRetailPrice(detail.getRetailPrice());
                goodsData.setBuyNumber(detail.getBuyNumber());
                goodsData.setSpecNames(detail.getSpecNames());
                goodsDataList.add(goodsData);
                totalBuyNumber += detail.getBuyNumber();
            }
            request.setTotalNumber(totalBuyNumber);
        }

        Map<String, String> stringMap = wrapSign(request, goodsDataList);
        //订单推送
        RestTemplate restTemplate = restTemplateBuilder.build();
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        stringMap.forEach((k,v) -> params.add(k,v));
        HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity(params,headers);
        ResponseEntity<String> responseEntity = restTemplate.postForEntity(unionPayUplanPropertis.getEndpoint(),httpEntity,String.class);
        log.info(String.format("response:%s",responseEntity.getBody()));
    }

    private Map<String, String> wrapSign(UnionPayOrderPushRequest request, List<UnionPayOrderPushGoodsData> goodsDataList) {
        ObjectMapper objectMapper = jacksonObjectMapperBuilder.build();
        Map<String, String> params = new HashMap<>();
        try {

            String json = objectMapper.writeValueAsString(request);
            DocumentContext documentContext = JsonPath.parse(json);
            Field[] fields = request.getClass().getDeclaredFields();
            for (Field field : fields) {
                String fieldName = field.getName();
                String fieldValue = documentContext.read(String.format("$.%s", fieldName), String.class);
                params.put(fieldName, fieldValue);
            }
            String goodsDateJson = objectMapper.writeValueAsString(goodsDataList);
            params.put("goodsData", goodsDateJson);
            String orginJson = unionPayUplanService.build(params);
            String resulutSign = Base64.getEncoder().encodeToString(unionPayUplanService.signData(orginJson));
            params.put("sign", resulutSign);
            log.info(String.format("推送订单请求:%s",objectMapper.writeValueAsString(params)));
        } catch (JsonProcessingException e) {
            log.error("序列化JSON异常", e);
        }
        return params;
    }

}
